package game

import (
	"fmt"
	"gogame/game/manage/config"
	cfg "gogame/game/manage/config/structs"
	"gogame/game/manage/model"
	"gogame/game/manage/module"
	"gogame/gameconfig"
	"gogame/gameservice/worker"
	"gogame/lib"
	"gogame/lib/database/mongo"
	"gogame/lib/database/redis"
	"gogame/logger"
	"gogame/pb"
	"gogame/server/rpcx"
	"regexp"
	"strings"
)

var (
	M    *module.ManageStruct // 功能模块manage
	GC   *config.ManageStruct // 配置manage
	DATA *model.ManageStruct  // 数据模型manage

	C      lib.CommonStruct  // 通用方法模块
	Event  *lib.EventEmitter // event
	Config gameconfig.Config // 区服配置
	Dfa    *lib.Dfa          // 敏感词检测dfa算法

	Redis      *redis.RedisStruct // redis
	Mdb        *mongo.MongoStruct // mongo
	CrossRedis *redis.RedisStruct // 跨服redis
	CrossMdb   *mongo.MongoStruct // 跨服mongo
)

func init() {
	M = module.Manage
	GC = config.Manage
	DATA = model.Manage

	C = lib.Common
	Event = lib.GlobalEvent
	Config = gameconfig.ServerConfig
	Dfa = lib.NewDfa(GC.Other().GetData())

	Redis = redis.InitRedis()
	Mdb = mongo.InitMongo()
	CrossRedis = redis.InitCrossRedis()
	CrossMdb = mongo.InitCrossMongo()

}

// L 语言包
func L(errMsgId string, keys ...any) string {
	_errMsg := GC.ErrorMsg().GetErrorMsg(errMsgId)
	if _errMsg == "" {
		logger.Print("错误码【%s】未定义!!!!", errMsgId)
	}

	if len(keys) > 0 {
		_errMsg = fmt.Sprintf(_errMsg, keys...)
	}

	return _errMsg
}

// FilterStr 过滤除中英文及数字意外的其他字符
func FilterStr(content string, ifBiaoDian bool) string {
	var re *regexp.Regexp
	// 排除标点
	if ifBiaoDian {
		re = regexp.MustCompile("[^\u4e00-\u9fa5^a-z^A-Z^0-9^]")
	} else {
		// 不排除标点
		re = regexp.MustCompile(`[^\\u4e00-\\u9fa5^a-z^A-Z^0-9^!@#$%&*() _+-=[]{}| ;'':"" ,./<>?^]`)
	}
	content = re.ReplaceAllString(content, "")

	return content
}

// ChkMsg 是否包含敏感词
func ChkMsg(content string) bool {
	if Dfa.Search(content) {
		return true
	}
	return false
}

// FilterMsg 敏感词替换为*
func FilterMsg(content string) string {
	sensitiveWords := Dfa.FindAll(content)
	for _, word := range sensitiveWords {
		content = strings.Replace(content, word, "*", -1)
	}
	return content
}

// GetGud 获取玩家gud
func GetGud(uid string) *pb.ModelUser {
	return DATA.UserInfo.GetGud(uid)
}

// RpcDo 通知rpcXClient执行x行为
func RpcDo(data any, metadata map[string]string, methodName, gatewayUrl string) {
	_service := worker.GetServiceByRand2Worker()
	if _service == nil {
		return
	}
	_service.RpcDo(gatewayUrl, methodName, metadata, data)
}

// SendCrossMsg 发送跨服消息
func SendCrossMsg(msg any) {
	_service := worker.GetServiceByRand2Worker()
	RpcDo(
		msg,
		nil,
		"SendCrossMsg",
		_service.RandTag(),
	)
}

// SendMsgByUid 推送消息到uid
func SendMsgByUid(uid string, msg any, gatewayUrl ...string) {
	// 消息不可为空
	if msg == nil {
		return
	}

	_uidGatewayUrl := ""
	if len(gatewayUrl) != 0 {
		_uidGatewayUrl = gatewayUrl[0]
	}

	// 从redis取
	if _uidGatewayUrl == "" {
		_uidGatewayUrl = GetUidGatewayUrl(uid)
	}

	// 玩家不在线
	if _uidGatewayUrl == "" {
		return
	}

	RpcDo(
		msg,
		map[string]string{"uid": uid},
		"SendMsgByUid",
		_uidGatewayUrl,
	)
}

// SendMsgByUidList 推送消息到uidList
func SendMsgByUidList(uidList []string, msg any) {
	// 消息不可为空
	if msg == nil {
		return
	}

	_uid2GatewayUrl := map[string]string{}
	for _, uid := range uidList {
		_gatewayUrl := GetUidGatewayUrl(uid)
		// 玩家不在线
		if _gatewayUrl == "" {
			continue
		}

		// 从redis取
		_uid2GatewayUrl[uid] = _gatewayUrl
	}

	RpcDo(
		msg,
		_uid2GatewayUrl,
		"SendMsgByUidList",
		"",
	)
}

// SendMsgByAll 推送消息到所有人
func SendMsgByAll(msg any) {
	// 消息不可为空
	if msg == nil {
		return
	}

	// 随机一个worker通知所有rpcXClient广播
	_service := worker.GetServiceByRand2Worker()
	if _service == nil {
		return
	}
	_service.RpcDoAll(
		"SendMsgByAll",
		nil,
		msg,
	)
}

// SendUidChangeInfo 推送玩家某些东西改变 不保证在接口返回前返回，多用于非接口文件中
func SendUidChangeInfo(uid string, changeInfo map[string]map[string]any, gatewayUrl ...string) {
	if len(changeInfo) == 0 {
		return
	}
	SendMsgByUid(uid, changeInfo, gatewayUrl...)
}

// SendConnChangeInfo 通过conn进行推送  保证在接口返回前返回，用于接口文件中
func SendConnChangeInfo(conn *rpcx.Response, changeInfo map[string]map[string]any) {
	if len(changeInfo) == 0 {
		return
	}
	conn.SendFirstJson(changeInfo)
}

type ChkDelNeedRes struct {
	Res bool   // 检测结果
	A   string // 不足的A
	T   string // 不足的T
}

// ChkDelNeed 检测消耗扣除
func ChkDelNeed(uid string, prizes []*cfg.Gameatn) ChkDelNeedRes {
	_chkDelNeedRes := ChkDelNeedRes{
		Res: true,
	}

	_prizeRes, _ := FmtPrizeRes(prizes)

	// 优先检测attr
	_attrT2n := _prizeRes["attr"]
	if len(_attrT2n) > 0 {
		_chkRes, _chkT := DATA.UserInfo.ChkAttrNum(uid, _attrT2n)
		// 检测不通过
		if !_chkRes {
			_chkDelNeedRes.Res = false
			_chkDelNeedRes.A = "attr"
			_chkDelNeedRes.T = _chkT
			return _chkDelNeedRes
		}
	}

	// 检测item
	_itemT2n := _prizeRes["item"]
	if len(_itemT2n) > 0 {
		_chkRes, _chkT := DATA.Item.ChkItemNum(uid, _itemT2n)
		// 检测不通过
		if !_chkRes {
			_chkDelNeedRes.Res = false
			_chkDelNeedRes.A = "item"
			_chkDelNeedRes.T = _chkT
			return _chkDelNeedRes
		}
	}

	return _chkDelNeedRes
}

// DelNeed 扣除消耗 使用前调用ChkDelNeed检测，通过后使用
func DelNeed(uid string, needs []*cfg.Gameatn, log map[string]any, gatewayUrl string, isSend bool) map[string]map[string]any {
	_res := map[string]map[string]any{
		"attr": {},
		"item": {},
	}
	_needRes := map[string]map[string]int32{
		"item": make(map[string]int32),
		"attr": make(map[string]int32),
	}
	for _, need := range needs {
		_a, _t, _n := need.A, need.T, need.N
		_aMap, ok := _needRes[_a]
		// 资源未定义
		if !ok {
			logger.WorkerLog.Warn(fmt.Sprintf("DelNeed error!! %s no del!!", _a))
			continue
		}

		_val, ok := _aMap[_t]
		if !ok {
			_aMap[_t] = _n
		} else {
			_aMap[_t] = _val + _n
		}

	}

	for a, t2n := range _needRes {
		if len(t2n) == 0 {
			continue
		}
		switch a {
		// 玩家属性改变
		case "attr":
			_setData := make(map[string]any)
			for t, n := range t2n {
				_setData[t] = -n
			}
			_updateRes := DATA.UserInfo.UpdateUserInfo(uid, _setData)
			for attrKey, val := range _updateRes {
				_res["attr"][attrKey] = val
			}
		// 扣除道具
		case "item":
			_objectId2Info := DATA.Item.DelItems(uid, t2n)
			for objectId, info := range _objectId2Info {
				_res["item"][objectId] = info
			}

		default:
			logger.WorkerLog.Warn(fmt.Sprintf("DelNeed 未定义【%s】扣除", a))
		}
	}

	// 记录消耗记录到gamelog
	DATA.GameLog.Add(uid, "need", needs, log)

	_changeInfo := FmtChangeInfo(_res)
	if isSend {
		SendUidChangeInfo(uid, _changeInfo, gatewayUrl)
	}

	return _changeInfo
}

// FmtPrizeRes 格式化奖励格式
func FmtPrizeRes(prizes []*cfg.Gameatn) (map[string]map[string]int32, []*pb.Prize) {
	_res := map[string]map[string]int32{
		"item": make(map[string]int32),
		"hero": make(map[string]int32),
		"attr": make(map[string]int32),
	}
	_prize := make([]*pb.Prize, len(prizes))
	for idx, prize := range prizes {
		_prize[idx] = &pb.Prize{A: prize.A, T: prize.T, N: prize.N}
		_a, _t, _n := prize.A, prize.T, prize.N
		_aMap, ok := _res[_a]
		// 资源未定义
		if !ok {
			continue
		}

		_val, ok := _aMap[_t]
		if !ok {
			_aMap[_t] = _n
		} else {
			_aMap[_t] = _val + _n
		}

	}

	return _res, _prize
}

// FmtChangeInfo 格式化推送数据
func FmtChangeInfo(changeInfo map[string]map[string]any) map[string]map[string]any {
	_changeInfo := make(map[string]map[string]any)
	for k, v := range changeInfo {
		if len(v) == 0 {
			continue
		}
		_changeInfo[fmt.Sprintf("%s_change", k)] = v
	}

	return _changeInfo
}

// GetPrizeRes 获取奖励
/*
	log: 领奖日志
		例如 领取任务奖励:
			log = {
				"act": "任务-领取任务奖励"
				"taskid": 任务id
			}
	isSend: 是否推送奖励导致的属性变化给前端
*/
func GetPrizeRes(uid string, prizes []*cfg.Gameatn, log map[string]any, gatewayUrl string, isSend bool) ([]*pb.Prize, map[string]map[string]any) {
	_prizeRes, _prize := FmtPrizeRes(prizes)

	_res := map[string]map[string]any{
		"attr": {},
		"hero": {},
		"item": {},
	}
	for a, t2n := range _prizeRes {
		switch a {
		// 玩家属性改变
		case "attr":
			_setData := make(map[string]any)
			for t, n := range t2n {
				_setData[t] = n
			}
			if len(_setData) > 0 {
				_updateRes := DATA.UserInfo.UpdateUserInfo(uid, _setData)
				for attrKey, val := range _updateRes {
					_res["attr"][attrKey] = val
				}
			}

		// 增加英雄
		case "hero":
			for hid, num := range t2n {
				i := int32(0)
				for ; i < num; i++ {
					_oid2Info := DATA.Hero.AddHero(uid, hid)
					for oid, info := range _oid2Info {
						_res["hero"][oid] = info
					}
				}
			}
		// 增加道具
		case "item":
			for itemId, num := range t2n {
				_oid2Info := DATA.Item.AddItem(uid, itemId, num)
				for oid, info := range _oid2Info {
					_res["item"][oid] = info
				}
			}
		default:
			logger.WorkerLog.Warn(fmt.Sprintf("GetPrizeRes 未定义【%s】使用", a))
		}
	}

	// 记录领奖记录到gamelog
	DATA.GameLog.Add(uid, "prize", prizes, log)

	_changeInfo := FmtChangeInfo(_res)
	// 推送
	if isSend {
		SendUidChangeInfo(uid, _res, gatewayUrl)
	}

	return _prize, _changeInfo
}
